/**
 * Moondance UML modeling tool
 * 
 * This program is free software; you can redistribute it and
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

package ro.pub.cs.se.moondance.command;

import java.util.Iterator;

import org.eclipse.gef.commands.Command;

import ro.pub.cs.se.moondance.model.Connection;
import ro.pub.cs.se.moondance.model.ConnectionFactory;
import ro.pub.cs.se.moondance.model.UMLEntity;
import ro.pub.cs.se.moondance.model.UMLClass;
import ro.pub.cs.se.moondance.model.UMLInterface;

/**
 * A command to create a connection between two UML blocks. The command can be
 * undone or redone.
 * <p>
 * This command is designed to be used together with a GraphicalNodeEditPolicy.
 * To use this command properly, following steps are necessary:
 * </p>
 * <ol>
 * <li>Create a subclass of GraphicalNodeEditPolicy.</li>
 * <li>Override the <tt>getConnectionCreateCommand(...)</tt> method, to create a
 * new instance of this class and put it into the CreateConnectionRequest.</li>
 * <li>Override the <tt>getConnectionCompleteCommand(...)</tt> method, to obtain
 * the Command from the ConnectionRequest, call setTarget(...) to set the target
 * endpoint of the connection and return this command instance.</li>
 * </ol>
 * 
 * @see ro.pub.cs.se.moondance.parts.UMLEditPart#createEditPolicies() for an
 *      example of the above procedure.
 * @see org.eclipse.gef.editpolicies.GraphicalNodeEditPolicy
 */
public class ConnectionCreateCommand extends Command {
	/** The connection instance. */
	private Connection connection;

	/** The desired connection type. */
	private final int connType;

	/** Start endpoint for the connection. */
	private final UMLEntity source;

	/** Target endpoint for the connection. */
	private UMLEntity target;

	/**
	 * Instantiate a command that can create a connection between two UML.
	 * 
	 * @param source
	 *            the source endpoint (a non-null Shape instance)
	 * @param lineStyle
	 *            the desired line style. See Connection#setLineStyle(int) for
	 *            details
	 * @throws IllegalArgumentException
	 *             if source is null
	 * @see Connection#setLineStyle(int)
	 */
	public ConnectionCreateCommand(UMLEntity source, int connType) {
		if (source == null)
			throw new IllegalArgumentException();
		setLabel("connection creation");
		this.source = source;
		this.connType = connType;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.commands.Command#canExecute()
	 */
	public boolean canExecute() {
		// disallow source -> source connections
		if (source.equals(target))
			if ((connType == Connection.INHERITANCE.intValue())
					|| (connType == Connection.REALIZATION.intValue()))
				return false;

		// inheritance association can be added only between 2 classes
		if (connType == Connection.INHERITANCE.intValue())
			if (!(((source instanceof UMLClass) && (target instanceof UMLClass)) || ((source instanceof UMLInterface) && (target instanceof UMLInterface))))
				return false;

		// realization association can be added only between a class and an
		// interface
		if (connType == Connection.REALIZATION.intValue())
			if (!((source instanceof UMLClass) && (target instanceof UMLInterface)))
				return false;

		// return false, if the source -> target connection exists already
		for (Iterator<Connection> iter = source.getSourceConnections()
				.iterator(); iter.hasNext();) {
			Connection conn = iter.next();
			if (conn.getTarget().equals(target))
				return false;
		}

		// return false, if the source -> target connection exists already
		for (Iterator<Connection> iter = source.getTargetConnections()
				.iterator(); iter.hasNext();) {
			Connection conn = iter.next();
			if (conn.getSource().equals(target)) {
				if ((conn.getType().equals(Connection.INHERITANCE) || conn
						.getType().equals(Connection.REALIZATION))
						&& ((connType == Connection.INHERITANCE.intValue()) || (connType == Connection.REALIZATION
								.intValue())))
					return false;
			}
		}

		return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.commands.Command#execute()
	 */
	public void execute() {
		// create a new connection between source and target
		// lineStyle contains the connection type;
		// Connection decodes the connection type and sets
		// line style accordingly
		connection = ConnectionFactory.newConnection(source, target, connType);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.commands.Command#redo()
	 */
	public void redo() {
		connection.reconnect();
	}

	/**
	 * Set the target endpoint for the connection.
	 * 
	 * @param target
	 *            that target endpoint (a non-null Shape instance)
	 * @throws IllegalArgumentException
	 *             if target is null
	 */
	public void setTarget(UMLEntity target) {
		if (target == null)
			throw new IllegalArgumentException();
		this.target = target;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.commands.Command#undo()
	 */
	public void undo() {
		connection.disconnect();
	}
}
